package hn.cch.advanced.concurrent;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;

public class CompletableFutureDemo {

    private static final Logger logger = LoggerFactory.getLogger(CompletableFutureDemo.class);


    public static void main(String[] args) {

        CompletableFuture< String > cf1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("执行step 1");
            return "step1 result";
        });
        CompletableFuture< String > cf2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("执行step 2");
            return "step2 result";
        });
        cf1.thenCombine(cf2, (result1, result2) -> {
            System.out.println(result1 + " , " + result2);
            System.out.println("执行step 3");
            return "step3 result";
        }).thenAccept(System.out::println);
    }

    public int sleep() {
        int seconds = (int) (Math.random() * 10);
        try {
            logger.debug("seconds:{}", seconds);
            Thread.sleep(seconds * 1000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return seconds;
    }

    @Test
    public void test0() {
        CompletableFuture< Void > f0 = CompletableFuture.runAsync(() -> {
            logger.debug("runAsync");
        });
        CompletableFuture< String > f1 = CompletableFuture.supplyAsync(() -> {
            logger.debug("supplyAsync");
            logger.debug("sleep:{}", sleep());
            return "supplyAsync";
        });
        CompletableFuture< String > f2 = CompletableFuture.completedFuture("completedFuture");

        logger.debug("join:{}", f1.join());

    }

    @Test
    public void test1() {
        CompletableFuture< String > fString = CompletableFuture.completedFuture("completedFuture");
        CompletableFuture< Integer > fInteger = CompletableFuture.completedFuture(10);

        CompletableFuture< Double > fApply = fString.thenApply(string -> string.length() * 0.1);
        logger.debug("thenApply:{}", fApply.join());

        CompletableFuture< Double > fCompose = fString.thenCompose(string ->
            CompletableFuture.supplyAsync(() -> string.length() * 0.1));
        logger.debug("thenApply:{}", fCompose.join());


    }


    @Test
    public void test2() {
        CompletableFuture< String > fString = CompletableFuture.supplyAsync(() -> String.valueOf(sleep()));
        CompletableFuture< Integer > fInteger = CompletableFuture.supplyAsync(this::sleep);
        CompletableFuture< Double > fCombine = fString.thenCombine(fInteger,
            (string, integer) -> Math.max(Integer.parseInt(string), integer) * 1.0);
        logger.debug("thenCombine:{}", fCombine.join());


        CompletableFuture< Integer > future0 = CompletableFuture.supplyAsync(this::sleep);
        CompletableFuture< Integer > future1 = CompletableFuture.supplyAsync(this::sleep);
        CompletableFuture< Double > applyToEither = future0.applyToEither(future1, integer -> integer * 1.0);
        logger.debug("applyToEither:{}", applyToEither.join());


    }

    @Test
    public void test3() {
        CompletableFuture< Integer > future = CompletableFuture.supplyAsync(() -> {
            int i = 0;
            return 1 / i;
        }).handle((integer, throwable) -> {
            if (Objects.isNull(throwable)) {
                return integer;
            } else {
                logger.error("handle:", throwable);
                return 0;
            }
        });
        logger.debug("handle:{}", future.join());


    }


}
